using System;
using System.Collections.Generic;
using System.Text;
using RoardersAgentSim.Map;
using RoardersAgentSim.Agent;
using System.Configuration;

namespace RoardersAgentSim
{
    public class Simulation
    {
        // input attributes
        public int max_time;    // maximum time steps in the simulation, or other termination criteria

        //----------------------------------------------------
        public PopulationManager populationManager;
		//----------------------------------------------------
        public DecisionMakers decisionMakers;
		//----------------------------------------------------
        public RoadLinkManager roadLinksManager;
		//----------------------------------------------------
        public ShelterManager sheltersManager;
		//----------------------------------------------------
		public AdviceManager advice;
		//----------------------------------------------------
		public Environment environment;


		#region constructor
		public Simulation()
		{
			populationManager = null;
			decisionMakers = null;
			roadLinksManager = null;
			sheltersManager = null;
			advice = null;
			environment = null;
		}
		#endregion
		//----------------------------------------------------
		/// <summary>
		/// Start the simulation
		/// </summary>
        public void Start()
        {
            LogHelper.LogStep("START");
            decisionMakers.ComputeShortestPaths(); // A2.3
            decisionMakers.FindOptimalResponse(); // A2.1
            LogHelper.Flush();
            Utils.FilePrefix = DateTime.Now.ToString("yyyyMMddHHmm");
            Utils.OutPutInitialised = false;
            for (int i = 1; i <= max_time; i++)
            {
                environment.time = i;
                LogHelper.LogStep("\tNew simulation step: " + i);
                roadLinksManager.ComputeLinkSpeed(); // A3.1
                LogHelper.Flush();
                decisionMakers.DecideIfReconsideringAdvice(); // A2.2
                populationManager.DetermineIfEvacuating(); // A1.1
                populationManager.Move(); // A1.4
                LogHelper.Flush();
                if(Utils.IsInDebugMode())
                    this.SaveState();


                // stop simulation when all shelters are full
                bool sheltersFull = true;
                foreach(Shelter sh in sheltersManager.Shelters)
                    if (!sh.IsFull)
                    {
                        sheltersFull = false;
                        break;
                    }

                if (sheltersFull)
                    break;
            }

            roadLinksManager.ExportTotalFlowState();
            roadLinksManager.ExportMaxFlowState();
            roadLinksManager.ExportCongestionFlowState(max_time);
            populationManager.ExportDS();
            decisionMakers.ExportPerformance();
            sheltersManager.ExportPerformance();
            LogHelper.LogStep("\nSimulation finished.");
            LogHelper.Flush();
        }
        /// <summary>
        /// save the state in their corresponding output files
        /// </summary>
        private void SaveState()
        {
            environment.SaveState(populationManager);
            sheltersManager.SaveState(this.environment.time);
        }

		// ---------------------------------------------------
		/// <summary>
		/// initialise the simulation
		/// </summary>
		/// <param name="map"></param>
		private void Init(FullMap map)
		{
			environment = new Environment(map);
			roadLinksManager = new RoadLinkManager(map);
			sheltersManager = new ShelterManager();
			advice = new AdviceManager();
			populationManager = new PopulationManager(environment, roadLinksManager, sheltersManager, advice);
			decisionMakers = new DecisionMakers(environment, populationManager, roadLinksManager, sheltersManager, advice);
			max_time = int.Parse(ConfigurationManager.AppSettings["MAXTIME"]);
		}
		#region public methods
		/// <summary>
		/// the main function 
		/// parse the map
		/// initialise the simulation
		/// start the simulation
		/// </summary>
        public void Initialise()
        {
            LogHelper.LogStep("REvaSim - ROADERS Evacuation Simulation, v1.0\n");
            LogHelper.LogStep("START Simulation");
            try
            {
                string mapDirectoryPath = ConfigurationManager.AppSettings["input"];
                FullMap map = new FullMap();
                CSVUtils.Initializer init = new CSVUtils.Initializer(mapDirectoryPath);
                map.Initialise(init);
                this.Init(map);
                // initialization can be done in constructors instead of "Initialize" methods
                environment.DefineScenarioAttributes();
                populationManager.Initialize(init);
                roadLinksManager.Initialize();
                sheltersManager.Initialize(map);
                roadLinksManager.InitializeShelterFlows(sheltersManager.FlowInitialisation());
                decisionMakers.Initialize();
                this.Start();

            }
            catch (Exception ex)
            {
                LogHelper.LogException(ex);
            }
            finally
            {
                LogHelper.LogStep("STOP Simulation");
                LogHelper.Close();
            }
		}
		#endregion
	}
}
